/**************************************************************************
 * Copyright (C) 2012 Tyler Robertson
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 **************************************************************************/

#ifndef __INPUT_H__
#define __INPUT_H__

#define ACTIVE_LOW		LOW
#define ACTIVE_HIGH		HIGH

struct DigitalReader
{
	inline unsigned char GetRawValue( const char pin ) const { return digitalRead( pin ); }
};

struct AnalogReader
{
	inline unsigned char GetRawValue( const char pin ) const { return analogRead( pin ); }
};

struct equal_to
{
	inline bool operator()( unsigned char lhs, unsigned char rhs ) const { return lhs == rhs; }
};

struct less_than
{
	inline bool operator()( unsigned char lhs, unsigned char rhs ) const { return lhs < rhs; }
};

template <unsigned char Pin, typename StateReader, typename Comparator, unsigned char ActiveLevel, unsigned char mode = INPUT>
class Input
{
public:
	void Initialize()
	{
		pinMode( Pin, mode );
		m_isActive[0] = m_isActive[1] = false;
		m_timeLastActive = 0;
		m_currIndex = 0;
		m_debounceDelay = 0;
		m_debounceTime = 0;
		m_debounceActive[0] = m_debounceActive[1] = false;
	}
	
	void SetDebounceDelay( unsigned int delay )
	{
		m_debounceDelay = delay;
	}
	
	void Reset()
	{
		m_timeLastActive = 0;
	}
	
	void Update()
	{
		m_currIndex ^= 1;

		m_debounceActive[ m_currIndex ] = m_cmp( GetRawValue(), ActiveLevel );
		
		if( m_debounceActive[ m_currIndex^1 ] != m_debounceActive[ m_currIndex ] )
		{
			m_debounceTime = millis();
		}
		
		if( millis() - m_debounceTime >= m_debounceDelay )
		{
			m_isActive[ m_currIndex ] = m_debounceActive[ m_currIndex ];
		}
		
		if( m_isActive[ m_currIndex ] )
		{
			m_timeLastActive = millis();
		}
	}

	
	unsigned char GetRawValue() const { return m_reader.GetRawValue( Pin ); }
	
	bool IsActive() const { return m_isActive[ m_currIndex ]; }
	bool DidJustActivate() const { return m_isActive[ m_currIndex ] && !m_isActive[ m_currIndex^1 ]; }
	bool DidJustDeactivate() const { return !m_isActive[ m_currIndex ] && m_isActive[ m_currIndex^1 ]; }
	unsigned long GetTimeLastActive() const { return m_timeLastActive; }
	
private:
	Comparator m_cmp;
	StateReader m_reader;
	unsigned int  m_debounceDelay;
	unsigned long m_debounceTime;
	unsigned long m_timeLastActive;
	unsigned char m_currIndex;
	bool m_isActive[2];
	bool m_debounceActive[2];
};



template <unsigned char Pin, unsigned char ActiveLevel=ACTIVE_HIGH>
struct DigitalInput : public Input< Pin, DigitalReader, equal_to, ActiveLevel >{};

template <unsigned char Pin, typename Comparator, unsigned char ActiveLevel>
struct AnalogInput : public Input< Pin, AnalogReader, Comparator, ActiveLevel >{};
		
template <unsigned char Pin>
struct DigitalPullupInput : public Input< Pin, DigitalReader, equal_to, LOW, INPUT_PULLUP >{};

#endif /* defined(__INPUT_H__) */
